﻿#include "pch.h"
#if _QG_ENABLE_WIDGET
#include "qgp/qgach.h"
#include "qgp/qgaud.h"
#include "qgp/qguwgs.h"

//////////////////////////////////////////////////////////////////////////
// 콤보
KIMPL_SUBCLASS(qwCombo, "WidgetCombo");

// _blabel = 한줄 라벨 영역
// _belem.ElemOpen = 열리면 목록 표시 영역
// _belem.ElemDown = 오른쪽 아래 화살표
// _blist = 열리면 항목 표시 영역, _belem.ElemOpen에서 margin, scrl 뺀 영역

class FakeCombo : public qwCombo
{
public:
	kint CalcSelection(kint y, kint& min, kint& max)
	{
		min = _scrl->GetPosition();
		max = min + _scrl->GetPageSize();

		return min + (y - _blist.top) / _height;
	}
};

//
qwCombo::qwCombo(qgWidgetType type/*=QSWGT_LIST*/)
	: super_type(type)
	, _current(-1)
	, _scrlsize(18)
	, _droplen(90)	// 18*5 = 5줄
	, _height(18)
	, _border(6, 6)
{
	_insts.input = true;
	_insts.fill = true;

	_text_info.align = QGWALIGN_VCENTER;

	_select.focus = -1;
	_select.margin.Set(5, 0);
}

//
qwCombo::~qwCombo()
{
	ClearItems();

	kobj_unload(_scrl);
}

//
void qwCombo::OnInitialize()
{
	super_type::OnInitialize();

	_scrl = (qwScroll*)_shed->CreateControl(QGW_SCROLL, -1, tpl::Rect(0, 0, 0, 0), NULL);
	_scrl->SetParent(this);

	_select.res[0] = _shed->GetResource(QGWELM_LIST_SELECT_ACTIVE);
	_select.res[1] = _shed->GetResource(QGWELM_LIST_SELECT_INACTIVE);
	_relem[0] = _shed->GetResource(QGWELM_COMBO_OPEN);
	_relem[1] = _shed->GetResource(QGWELM_COMBO_DOWN);
}

//
void qwCombo::OnChangeFocus(bool focus)
{
	_isfocus = focus;

	if (focus)
	{
		_isopen = true;

		// 컴보는 바운더리가 확장되는 구조로 열릴 때는 실제 보통 바운더리를 
		// _breal에 넣어 두고 _bound를 _droplen 만큼 늘린다
		_bsave = _bound;
		_bound.bottom += _droplen;

		_select.focus = -1;

		if (_ecb_context_open)
			_ecb_context_open(this, true);
	}
	else
	{
		_isopen = false;

		// 원래 늘렸던 바운드를 복구한다
		_bound = _bsave;

		if (_ecb_context_close)
			_ecb_context_close(this, false);
	}

	SetRefresh();
}

//
bool qwCombo::OnUpdate(float adv)
{
	bool boundrefresh = false;

	BuildBound(_isopen ? false : true);

	if (NeedRefresh())
	{
		tpl::Rect rt;
		kint height;

		// 바운더리 조절
		if (_autoheight)
		{
			// 아이템 높이 또는 글자 크기에 맞춰 라벨 부분 높이를 맞춘다
			height = _height > 0 ? _height : _text_info.size;

			if (_isopen)
			{
				rt = _bsave;

				boundrefresh = true;
			}
			else
			{
				_bound.bottom = _bound.top + height;
				BuildBound();

				rt = _client;
			}
		}
		else
		{
			// 사용자가 지정한 높이로 맞춘다
			if (!_isopen)
				rt = _client;
			else
			{
				rt = _bsave;

				boundrefresh = true;
			}

			height = rt.Height();
		}

		_belem[ElemDown].Set(_client.right - height, _client.top, _client.right, _client.top + height);
		_blabel.Set(_client.left, _client.top, _client.right - height, _client.top + height);

		// 라벨 텍스트 앵커
		_text_info.anch = CalcAlign(_text_info.align, tpl::Point(2, _text_info.size), rt);

		// 아이템 앵커
		_anchor.y = (_height - _text_info.size) / 2;

		if (_anchor.y < 0)
			_anchor.y = 0;

		//
		ResetRefresh();
	}

	if (boundrefresh)
	{
		// bnd는 원래 표시할 한줄 영역. 이미 _bound, _client는 확장했을 수도 있기 때문에
		//tpl::Rect bnd = CalcBound(_isopen ? _bsave : _bound);
		kint hdisp = _blabel.Height();
		kint hbound = _bound.Height();

		_belem[ElemOpen] = _client.Offset(0, hdisp, 0, 0);
		_select.bound = _belem[ElemOpen].Deflate(_border.x, _border.y, _border.x + _scrlsize, _border.y);
		_blist = _select.bound.Deflate(_select.margin.x, _select.margin.y);

		// 스크롤 크기 및 페이지 조절
		kint w = _bsave.Width();
		_scrl->SetBound(w - _scrlsize, hdisp, _scrlsize, hbound - hdisp);

		if (_height > 0)
		{
			_scrl->SetPageSize(_blist.Height() / _height);
			//_scrl->SetPosition(_current);
		}
	}

	_scrl->OnUpdate(adv);

	return true;
}

//
void qwCombo::OnDraw()
{
	const qbWidgetResource& rnorm = _res[_status == QGWS_DISABLE ? QGWS_DISABLE : QGWS_NORMAL];

	// 열림 영역 채움
	if (_isopen)
		_shed->DrawFillRect(_belem[ElemOpen], _relem[ElemOpen]);

	// 라벨 영역 채움
	if (CanFill())
		_shed->DrawFillRect(_blabel, rnorm, _tex);

	// 라벨 영역 오른쪽 화살표
	_shed->DrawFillRect(_belem[ElemDown], _relem[ElemDown], _tex);

	// 열리면 목록 표시
	if (_isopen)
	{
		if (_items.IsHave())
		{
			qbWidgetTextInfo ti = _text_info;
			ti.anch.Set(0, 0);

			tpl::Rect rttxt(_blist);
			kint height = _height;

			rttxt.top += _anchor.y;
			rttxt.bottom = rttxt.top + height;

			for (kint i = _scrl->GetPosition(); i < _items.Count(); i++)
			{
				if ((rttxt.bottom - _border.y) > _blist.bottom)
					break;

				Item* node = _items[i];

				// 포커스 또는 선택 표시
				if (i == _current)
					_shed->DrawFillRect(rttxt.Offset(0, -_anchor.y), _select.res[0], _tex);
				else if (i == _select.focus)
					_shed->DrawFillRect(rttxt.Offset(0, -_anchor.y), _select.res[1], _tex);

				_shed->DrawAnchText(_font, node->str.Data(), rttxt, ti, 0);

				rttxt.top += height;
				rttxt.bottom += height;
			}
		}

		_scrl->OnDraw();
	}

	// 현재 아이템 -> 라벨 영역에 표시
	if (_current >= 0)
	{
		Item* node = _items[_current];
		_shed->DrawAnchText(_font, node->str.Data(), _blabel, _text_info, 0);
	}

	// 외곽선
	if (HasBorder())
	{
		_shed->DrawEmptyRect(_blabel, _color_border);

		if (_isopen)
			_shed->DrawEmptyRect(_belem[ElemOpen], _color_border);
	}

	//
	qwCtrl::OnDraw();
}

//
void qwCombo::ClearItems()
{
	kForEach(Item* i, _items)
	{
		// 다른건 없고...
		delete i;
	}

	_items.Clear();

	//
	_current = -1;

	_scrl->SetCount(1);

	if (_ecb_select_changed)
		_ecb_select_changed(this, _current);
}

//
bool qwCombo::AddItem(const char* str, kpointer tag/*=NULL*/)
{
	k_return_value_if_fail(str, false);

	Item* node = new Item();
	node->str = str;
	node->tag = tag;
	node->color = _text_info.color;
	_items.Add(node);

	_scrl->SetCount(_items.Count());

	if (_current < 0)
		_current = 0;

	return true;
}

//
bool qwCombo::AddItem(const char* str, const tpl::ColorValue& color, kpointer tag/*=NULL*/)
{
	k_return_value_if_fail(str, false);

	Item* node = new Item();
	node->str = str;
	node->tag = tag;
	node->color = color;
	_items.Add(node);

	_scrl->SetCount(_items.Count());

	if (_current < 0)
		_current = 0;

	return true;
}

//
bool qwCombo::InsertItem(kint nth, const char* str, kpointer tag/*=NULL*/)
{
	k_return_value_if_fail(str, false);

	Item* node = new Item();
	node->str = str;
	node->tag = tag;
	node->color = _text_info.color;

	if (!_items.Insert(nth, node))
	{
		delete node;
		return false;
	}

	_scrl->SetCount(_items.Count());

	if (_current < 0)
		_current = 0;

	return true;
}

//
bool qwCombo::InsertItem(kint nth, const char* str, const tpl::ColorValue& color, kpointer tag/*=NULL*/)
{
	k_return_value_if_fail(str, false);

	Item* node = new Item();
	node->str = str;
	node->tag = tag;
	node->color = color;

	if (!_items.Insert(nth, node))
	{
		delete node;
		return false;
	}

	_scrl->SetCount(_items.Count());

	if (_current < 0)
		_current = 0;

	return true;
}

//
bool qwCombo::RemoveItem(kint nth)
{
	k_return_value_if_fail((ksize_t)nth < (ksize_t)_items.Count(), false);

	Item* node = _items[nth];
	delete node;

	_items.RemoveAt(nth);

	_scrl->SetCount(_items.Count());

	if (_current >= _items.Count())
	{
		_current = _items.Count() - 1;

		if (_ecb_select_changed)
			_ecb_select_changed(this, _current);
	}

	return true;
}

//
qwCombo::Item* qwCombo::GetItem(kint nth)
{
	return (ksize_t)nth < (ksize_t)_items.Count() ? _items[nth] : NULL;
}

//
const qbWidgetResource& qwCombo::GetResource(qgWidgetElement nth)
{
	switch ((ksize_t)nth)
	{
		case QGWELM_LIST_SELECT_ACTIVE:
			return _select.res[0];

		case QGWELM_LIST_SELECT_INACTIVE:
			return _select.res[1];

		case QGWELM_COMBO_OPEN:
			return _relem[ElemOpen];

		case QGWELM_COMBO_DOWN:
			return _relem[ElemDown];
	}

	return super_type::GetResource(nth);
}

//
void qwCombo::SetSelectedIndex(kint nth)
{
	if (_items.IsEmpty())
		return;

	kint prev = _current;

	if (nth < 0)
		nth = 0;
	else if (nth >= _items.Count())
		nth = _items.Count() - 1;

	_current = nth;

	if (prev != nth)
		_scrl->SetPosition(nth);

	//  if (_fn_sel_chgd)
	//      _fn_sel_chgd(this, HB_CAST_INT_TO_PTR(_current));
}

//
bool qwCombo::SetResource(qgWidgetElement nth, const qbWidgetResource& res)
{
	switch ((ksize_t)nth)
	{
		case QGWELM_LIST_SELECT_ACTIVE:
			_select.res[0] = res;
			return true;

		case QGWELM_LIST_SELECT_INACTIVE:
			_select.res[1] = res;
			return true;

		case QGWELM_COMBO_OPEN:
			_relem[ElemOpen] = res;
			return true;

		case QGWELM_COMBO_DOWN:
			_relem[ElemDown] = res;
			return true;
	}

	return super_type::SetResource(nth, res);
}

//
bool qwCombo::OnMouseMove(kint x, kint y, const tpl::Point& relative, kushort state)
{
	if (!_scrl->OnMouseMove(x, y, relative, state))
		return false;

	if (_isopen && _items.IsHave() && _select.bound.OnPoint(x, y))
	{
		// 열림, 아이템 있을 때 마우스 움직이면 보여줌
		kint min, max, nth = static_cast<FakeCombo*>(this)->CalcSelection(y, min, max);

		if (nth >= min && nth <= max && nth < _items.Count())
			_select.focus = nth;
	}

	return true;
}

//
bool qwCombo::OnMouseDown(kint x, kint y, kushort button)
{
	if (!_scrl->OnMouseDown(x, y, button))
		return false;

	if (button == QIM_LEFT)
	{
		if (_isopen)
		{
			tpl::Rect rt(_blabel);

			if (rt.OnPoint(x, y) || _belem[ElemDown].OnPoint(x, y))
			{
				// 닫자
				_shed->SetFocus(NULL);
			}
			else if (_items.IsHave() && _select.bound.OnPoint(x, y))
			{
				kint min, max, nth = static_cast<FakeCombo*>(this)->CalcSelection(y, min, max);

				if (nth >= min && nth <= max && nth < _items.Count())
				{
					_current = nth;
					_select.focus = nth;

					SetRefresh();
				}
			}
		}
		else
		{
			// 닫혀 있으면... 연다
			_shed->SetFocus(this);
		}

		return false;
	}

	return true;
}

//
bool qwCombo::OnMouseUp(kint x, kint y, kushort button)
{
	if (!_scrl->OnMouseUp(x, y, button))
		return false;

	if (button == QIM_LEFT)
	{
		if (_isopen && _items.IsHave() && _select.bound.OnPoint(x, y))
		{
			kint min, max, nth = static_cast<FakeCombo*>(this)->CalcSelection(y, min, max);

			if (nth >= min && nth <= max && nth < _items.Count())
			{
				_current = nth;
				//_select.focus = nth;	// 닫을 건데 세팅해서 뭐함

				// 먼저 쏘고
				if (_ecb_select_changed)
					_ecb_select_changed(this, nth);

				// 닫자
				_shed->SetFocus(NULL);

				SetRefresh();
			}

			return false;
		}
	}

	return true;
}

//
bool qwCombo::OnMouseWheel(kshort delta)
{
	_scrl->Scroll(-delta);
	return false;
}

//
bool qwCombo::OnKeyDown(kbyte key, kbyte sub, kuint ch, bool repeat, kushort state)
{
	bool ret = true;

	if (key == QIK_PGUP)
	{
		_scrl->Scroll(-_scrl->GetPageSize());
		ret = false;
	}
	else if (key == QIK_PGDN)
	{
		_scrl->Scroll(+_scrl->GetPageSize());
		ret = false;
	}

	// 이거 항목 선택 & 스크롤도 만들어야됨

	return ret;
}

#endif	// _QG_ENABLE_WIDGET
